;================================================================
;  math.s
;               Mathematical functions
;
;================================================================
;
; 25thanni, a demo dedicated to the 25th anniversary of the ZX81.
;
; (c)2006 Bodo Wenzel
;
; This program is free software; you can redistribute it and/or
; modify it under the terms of the GNU General Public License as
; published by the Free Software Foundation; either version 2 of
; the License, or (at your option) any later version.
;
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
; GNU General Public License for more details.
;
; You should have received a copy of the GNU General Public
; License along with this program; if not, write to the Free
; Software Foundation Inc., 59 Temple Place, Suite 330, Boston,
; MA 02111-1307 USA
;================================================================

	.module	math

;= Constants ====================================================

MATH_VALUE_SIZE	==	7
MATH_SIGN	==	0x80
MATH_TWO_PI	==	256

;= Program code =================================================

	.area	CODE

;- Multiply two unsigned values ---------------------------------
; A	factor1		0 .. 255 = 0.0 .. 1.99
; E	factor2		0 .. 255 = 0.0 .. 1.99
; A	product		0 .. 255 = 0.0 .. 1.99

	.if	0		; not used  - - - - - - - - - - -

product::
	push	bc
	push	de
	push	hl
	ld	hl,#0
	ld	d,h
	.if	MATH_VALUE_SIZE-7
	.error	"Code depends on MATH_VALUE_SIZE=7"
	.endif
	ld	b,#MATH_VALUE_SIZE+2
p_loop:
	add	hl,hl
	jr	nc,p_add
	ld	a,#(2<<MATH_VALUE_SIZE)-1
	jr	p_quit
p_add:
	rla
	jr	nc,p_next
	add	hl,de
p_next:
	djnz	p_loop
	ld	a,h
p_quit:
	pop	hl
	pop	de
	pop	bc
	ret

	.endif			; not used  - - - - - - - - - - -

;- Divide two unsigned values -----------------------------------
; A	dividend	0 .. 255 = 0.0 .. 1.99
; E	divisor		0 .. 255 = 0.0 .. 1.99
; A	quotient	0 .. 255 = 0.0 .. 1.99

quotient::
	push	bc
	push	hl
	ld	h,a
	.if	MATH_VALUE_SIZE-7
	.error	"Code depends on MATH_VALUE_SIZE=7"
	.endif
	xor	a
	ld	l,a
	rr	h
	rr	l
	ld	b,#MATH_VALUE_SIZE+1
q_loop:
	add	hl,hl
	ld	a,h
	jr	c,q_sub
	sub	e
	jr	c,q_next
q_inc:
	ld	h,a
	inc	l
q_next:
	djnz	q_loop
	ld	a,l
	jr	q_quit
q_sub:
	sub	e
	jr	c,q_inc
	ld	a,#(2<<MATH_VALUE_SIZE)-1
q_quit:
	pop	hl
	pop	bc
	ret

;- Obtain the cosine --------------------------------------------
; A	input		0 .. 255 = 0 .. 2*PI-0.02
; A	output		-128 .. +127 = -0.99 .. +0.99

	.if	0		; not used  - - - - - - - - - - -

cosine::
	add	a,#MATH_TWO_PI/4; add offset
	; falls through to sine

	.endif			; not used  - - - - - - - - - - -

;- Obtain the sine ----------------------------------------------
; A	input		0 .. 255 = 0 .. 2*PI-0.02
; A	output		-128 .. +127 = -0.99 .. +0.99

sine::
	push	hl
	bit	7,a
	push	af		; check for second half
	bit	6,a
	jr	z,s_mask
	cpl			; mirror for second quarter
s_mask:
	and	#MATH_TWO_PI/4-1
	ld	hl,#sinetab
	add	a,l
	ld	l,a
	jr	nc,s_quit
	inc	h
s_quit:
	pop	af
	ld	a,(hl)
	pop	hl
	ret	z
	cpl			; mirror for second half
	ret

;- Obtain the arc cosine ----------------------------------------
; A	input		-128 .. +127 = -0.99 .. +0.99
; A	output		0 .. 127 = 0 .. +PI-0.02

arccos::
	push	hl
	bit	7,a
	push	af		; check for second half
	jr	z,ac_get
	cpl			; mirror for second half
ac_get:
	ld	hl,#acostab
	add	a,l
	ld	l,a
	jr	nc,ac_quit
	inc	h
ac_quit:
	pop	af
	ld	a,(hl)
	pop	hl
	ret	z
	xor	#MATH_TWO_PI/2-1; mirror for second half
	ret

;- Obtain the arc sine ------------------------------------------
; A	input		-128 .. +127 = -0.99 .. +0.99
; A	output		-64 .. +63 = -PI/2 .. +PI/2-0.02

	.if	0		; not used  - - - - - - - - - - -

arcsin::
	call	arccos
	cpl
	add	a,#MATH_TWO_PI/4; subtract from offset
	ret

	.endif			; not used  - - - - - - - - - - -

;= Variables ====================================================

sinetab:
	.include	"sinetab.inc"

acostab:
	.include	"acostab.inc"

;= The end ======================================================
